package com.think.cloud.thinkshop.mall.service.order.impl;


import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.think.cloud.thinkshop.common.constant.order.OrderConstants;
import com.think.cloud.thinkshop.common.enums.RabbitMessageTypeEnum;
import com.think.cloud.thinkshop.common.enums.payment.PaymentStatusEnum;
import com.think.cloud.thinkshop.common.enums.user.SwitchEnum;
import com.think.cloud.thinkshop.mall.controller.admin.message.dto.MessageTemplateReplaceDTO;
import com.think.cloud.thinkshop.mall.controller.admin.productcomment.param.ProductCommentAddParam;
import com.think.cloud.thinkshop.mall.controller.admin.shippingtemplates.dto.FreightComputeDTO;
import com.think.cloud.thinkshop.mall.controller.app.integral.vo.IntegralRuleVO;
import com.think.cloud.thinkshop.mall.controller.app.coupon.dto.CartCouponDTO;
import com.think.cloud.thinkshop.mall.controller.app.coupon.vo.CartCouponDetailRespVO;
import com.think.cloud.thinkshop.mall.controller.app.coupon.vo.CartCouponRespVO;
import com.think.cloud.thinkshop.mall.controller.app.order.dto.AppOrderInfoDTO;
import com.think.cloud.thinkshop.mall.controller.app.order.dto.AppOrderLogDTO;
import com.think.cloud.thinkshop.mall.controller.app.order.vo.*;
import com.think.cloud.thinkshop.mall.convert.integral.IntegralConvert;
import com.think.cloud.thinkshop.mall.convert.order.OrderConvert;
import com.think.cloud.thinkshop.mall.domain.LogisticsLog;
import com.think.cloud.thinkshop.mall.domain.ProductComment;
import com.think.cloud.thinkshop.mall.domain.ShopCart;
import com.think.cloud.thinkshop.mall.domain.UserAddress;
import com.think.cloud.thinkshop.mall.domain.coupon.ProductCouponRelation;
import com.think.cloud.thinkshop.mall.domain.integral.IntegralRule;
import com.think.cloud.thinkshop.mall.domain.memberuser.MemberUser;
import com.think.cloud.thinkshop.mall.domain.order.Order;
import com.think.cloud.thinkshop.mall.domain.order.OrderDetail;
import com.think.cloud.thinkshop.mall.domain.order.OrderLog;
import com.think.cloud.thinkshop.mall.domain.websitesetting.WebsiteSetting;
import com.think.cloud.thinkshop.mall.enums.common.CommonWhetherEnum;
import com.think.cloud.thinkshop.mall.enums.integral.IntegralBillTypeEnum;
import com.think.cloud.thinkshop.mall.enums.integral.IntegralRuleDeductionTypeEnum;
import com.think.cloud.thinkshop.mall.enums.message.MessageTemplateEnum;
import com.think.cloud.thinkshop.mall.enums.order.OrderOperateTypeEnum;
import com.think.cloud.thinkshop.mall.enums.order.OrderOperateUserTypeEnum;
import com.think.cloud.thinkshop.mall.enums.order.OrderStatusEnum;
import com.think.cloud.thinkshop.mall.enums.order.OrderUseIntegralEnum;
import com.think.cloud.thinkshop.mall.mapper.order.OrderMapper;
import com.think.cloud.thinkshop.mall.rabbit.producer.MessageProducer;
import com.think.cloud.thinkshop.mall.service.address.AppUserAddressService;
import com.think.cloud.thinkshop.mall.service.aftersales.AppAfterSalesService;
import com.think.cloud.thinkshop.mall.service.cart.AppShopCartService;
import com.think.cloud.thinkshop.mall.service.coupon.AppProductCouponRelationService;
import com.think.cloud.thinkshop.mall.service.integral.AppIntegralService;
import com.think.cloud.thinkshop.mall.service.memberuser.IMemberUserService;
import com.think.cloud.thinkshop.mall.service.message.IMessageTemplateService;
import com.think.cloud.thinkshop.mall.service.order.AppOrderDetailService;
import com.think.cloud.thinkshop.mall.service.order.AppOrderLogService;
import com.think.cloud.thinkshop.mall.service.order.AppOrderService;
import com.think.cloud.thinkshop.mall.service.order.ILogisticsLogService;
import com.think.cloud.thinkshop.mall.service.product.AppProductService;
import com.think.cloud.thinkshop.mall.service.productcomment.AppProductCommentService;
import com.think.cloud.thinkshop.mall.service.shippingtemplates.IShippingTemplatesService;
import com.think.cloud.thinkshop.mall.service.websitesetting.IWebsiteSettingService;
import com.think.cloud.thinkshop.mall.utils.RedisUtils;
import com.think.common.core.exception.util.ServiceExceptionUtil;
import com.think.common.core.model.LoginUser;
import com.think.common.core.utils.DateUtils;
import com.think.common.core.utils.PageUtils;
import com.think.common.core.web.page.TableDataInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.think.common.core.exception.enums.ErrorCode.*;

/**
 * 订单AppService业务层处理
 *
 * @author zkthink
 * @date 2024-05-24
 */
@Service
@Slf4j
public class AppOrderServiceImpl implements AppOrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private AppShopCartService appShopCartService;

    @Autowired
    private IWebsiteSettingService websiteSettingService;

    @Autowired
    private AppProductCouponRelationService appProductCouponRelationService;

    @Autowired
    private IShippingTemplatesService iShippingTemplatesService;

    @Autowired
    private AppUserAddressService appUserAddressService;

    @Autowired
    private AppOrderDetailService appOrderDetailService;

    @Autowired
    private AppOrderLogService appOrderLogService;

    @Autowired
    private AppProductService appProductService;

    @Autowired
    private IMemberUserService memberUserService;

    @Autowired
    private MessageProducer messageProducer;

    @Autowired
    private ILogisticsLogService logisticsLogService;

    @Autowired
    private AppProductCommentService productCommentService;

    @Autowired
    private AppIntegralService integralService;

    @Autowired
    private IMessageTemplateService messageTemplateService;

    @Autowired
    private AppAfterSalesService appAfterSalesService;

    @Override
    public AppConfirmOrderRespVO confirm(AppConfirmOrderReqVO vo) {
        // 查询购物车信息
        List<AppOrderCartRespVO> carts = getOrderCartInfo(vo.getCartIds());
        if (CommonWhetherEnum.YES.getValue().equals(vo.getType()))
            return AppConfirmOrderRespVO.builder().totalPrice(computeTotalPrice(carts)).build();
        // 查询配置
        AppOrderInfoDTO dto = this.computeOrder(carts, vo, this.getSettingCache());
        // 计算总税费  = 基本税费+运费税费
        dto.setTaxation(dto.getTaxation().add(dto.getPostageTaxation()));
        AppConfirmOrderRespVO result = OrderConvert.INSTANCE.convert(dto);
        // 返回选中地址和优惠券
        result.setCouponId(vo.getCouponId());
        result.setAddressId(vo.getAddressId());
        result.setCarts(carts);
        result.setAddressValid(vo.getAddressValid());
        return result;
    }

    /**
     * 构建积分使用选项
     *
     * @param rule
     * @param integralBalance
     * @param payPrice
     * @return
     */
    private static List<BigDecimal> buildUseIntegralOptions(IntegralRuleVO rule, BigDecimal integralBalance,
                                                            BigDecimal payPrice) {
        List<BigDecimal> options = new ArrayList<>();
        if (rule.getIsUse().equals(OrderUseIntegralEnum.NO_USE.getCode()) || integralBalance.compareTo(BigDecimal.ZERO) == 0) {
            return options;
        }
        BigDecimal deductionRatio = rule.getDeductionRatio();//抵扣比值 1积分可以换xx元
        Integer deductionType = rule.getDeductionType();  //1；最多积分 ，2：最高比例
        BigDecimal deductionValue = rule.getDeductionValue(); // 抵扣类型对应的值

        if (deductionType.equals(IntegralRuleDeductionTypeEnum.MAX_INTEGRAL.getCode())) {
            //最大可被用到的积分值
            BigDecimal maxUseIntegral = deductionValue.compareTo(integralBalance) > 0 ? integralBalance : deductionValue;
            int i = 0;
            while (true) {
                //积分值
                BigDecimal integral = new BigDecimal((i + 1) * 100);
                //抵扣金额
                BigDecimal value = integral.multiply(deductionRatio);
                if (integral.compareTo(maxUseIntegral) > 0 || value.compareTo(payPrice) > 0) {
                    break;
                }
                options = Arrays.asList(integral, value);
                i++;
            }
        }
        if (deductionType.equals(IntegralRuleDeductionTypeEnum.MAX_SCALE.getCode())) {
            BigDecimal oneHundred = new BigDecimal("100");
            int i = 5;
            while (true) {
                //比例值
                BigDecimal scale = new BigDecimal(i).divide(oneHundred, 2, RoundingMode.DOWN);
                //金额
                BigDecimal amount = payPrice.multiply(scale).setScale(2, RoundingMode.DOWN);
                //积分值
                BigDecimal integral = amount.divide(deductionRatio, 0, RoundingMode.DOWN);
                //复算金额
                amount = integral.multiply(deductionRatio);
                if (scale.compareTo(deductionValue.divide(oneHundred, 2, RoundingMode.UP)) > 0
                        || integral.compareTo(integralBalance) > 0
                        || amount.compareTo(BigDecimal.ZERO) == 0) {
                    break;
                }
                options = Arrays.asList(integral, amount);
                i += 5;
            }
        }
        return options;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long create(AppCreateOrderReqVO vo) {
        // 查询购物车信息
        List<AppOrderCartRespVO> carts = getOrderCartInfo(vo.getCartIds());
        // 查询配置
        WebsiteSetting setting = this.getSettingCache();
        // 构建订单
        Order order = buildOrder(carts, vo, setting);
        Long userId = vo.getUserId();
        //检查并扣除积分
        if (!integralService.reduceUserIntegral(userId, order.getUseIntegral())) {
            throw ServiceExceptionUtil.exception(INTEGRAL_BALANCE_ERROR);
        }
        // 保存订单
        orderMapper.insert(order);
        // 处理库存
        this.handleStock(carts, CommonWhetherEnum.YES);
        Long id = order.getId();
        // 保存订单明细
        appOrderDetailService.batchInsert(carts, id);
        // 购物车修改状态
        this.cartPayUpdate(vo.getCartIds());
        // 修改优惠券使用状态
        if (ObjectUtil.isNotNull(vo.getCouponId()))
            appProductCouponRelationService.verificationCoupon(vo.getCouponId());
        // 订单日志保存
        this.saveOrderLog(id, userId, vo.getUserName(), OrderOperateTypeEnum.SUBMIT_ORDER);
        //订单超时消息
        Integer orderCancelHour = setting.getOrderCancelHour();
        messageProducer.sendDelayedMessage(id.toString(),
                RabbitMessageTypeEnum.DELAY_ORDER_CANCEL_HOUR,
                orderCancelHour,
                TimeUnit.HOURS
        );
        //创建订单 后通知付款，订单超时时间小于1小时，则30分钟后通知 ，大于1小时，则一小时后通知
        long delay = 0;
        TimeUnit unit = null;
        if (orderCancelHour <= 1) {
            delay = 30;
            unit = TimeUnit.MINUTES;
        } else {
            delay = 1;
            unit = TimeUnit.HOURS;
        }
        messageProducer.sendDelayedMessage(id.toString(),
                RabbitMessageTypeEnum.DELAY_ORDER_WAIT_PAY_NOTICE, delay, unit
        );
        //触发自动客户分群
        messageProducer.sendMessage(String.valueOf(userId), RabbitMessageTypeEnum.MEMBER_GROUP_UPDATE_BY_USER_ACTION);
        // 返回订单id
        return id;
    }

    //订单规则配置
    private WebsiteSetting getSettingCache() {
        return websiteSettingService.getCache();
    }

    // 处理库存
    private void handleStock(List<AppOrderCartRespVO> carts, CommonWhetherEnum whetherEnum) {
        appProductService.handleStock(carts, whetherEnum.getValue());
    }

    // 购物车修改状态
    private void cartPayUpdate(List<Long> cartIds) {
        appShopCartService.cartsPay(cartIds, CommonWhetherEnum.YES.getValue());
    }

    /**
     * 构建订单
     *
     * @param carts
     * @param vo
     * @return
     */
    private Order buildOrder(List<AppOrderCartRespVO> carts, AppCreateOrderReqVO vo, WebsiteSetting setting) {
        AppOrderInfoDTO dto = computeOrder(carts, vo, setting);
        Long userId = vo.getUserId();
        Long couponId = vo.getCouponId();
        // 查询地址信息
        UserAddress address = this.findUserAddress(vo.getAddressId(), userId);
        if (ObjectUtil.isNull(address)) throw ServiceExceptionUtil.exception(ADDRESS_NOT_EXIST_ERROR);
        Order order = new Order();
        order.setOrderCode(getOrderCode()); // 生成商品编号
        order.setUserId(userId);    // 设置用户id
        order.setCouponId(couponId);    // 设置优惠券id
        ProductCouponRelation couponRelation = appProductCouponRelationService.getById(couponId);
        order.setPlanId(Objects.isNull(couponRelation) ? null : couponRelation.getPlanId());    //运营计划id
        order.setFirstName(address.getFirstName());
        order.setLastName(address.getLastName());
        order.setUserPhone(address.getPhone());
        order.setCountry(address.getCountry()); // 设置国家
        order.setRegionId(address.getProvinceId()); // 设置地区id
        // 设置地址
        order.setAddressDetail(address.getCountry() + address.getProvince() + address.getCity() + address.getDetail());
        order.setPostCode(address.getPostCode());   // 设置邮编
        order.setTotalNum(dto.getTotalNum());   // 设置商品总数
        order.setTotalPrice(dto.getTotalPrice());   // 设置商品总价
        order.setTotalPostage(dto.getTotalPostage());   // 设置运费
        order.setCouponPrice(dto.getDiscountAmount());   // 设置优惠金额
        order.setTaxation(dto.getTaxation());   // 设置税费
        order.setPostageTaxation(dto.getPostageTaxation());   // 设置运费税费
        order.setPayPrice(dto.getPayPrice());   // 设置支付金额
        order.setMark(vo.getMark());  // 设置备注
        //积分相关
        order.setUseIntegral(dto.getPayIntegral() != null ? dto.getPayIntegral().intValue() : 0);
        order.setIntegralDeduct(dto.getIntegralDeduct());
        return order;
    }

    // 查询用户地址信息
    private UserAddress findUserAddress(Long addressId, Long userId) {
        return appUserAddressService.selectDefaultAddress(addressId, userId);
    }

    /**
     * 构建订单操作记录
     *
     * @param dto
     * @return
     */
    private OrderLog buildOrderLog(AppOrderLogDTO dto) {
        return OrderLog.builder().orderId(dto.getOrderId()).userId(dto.getUserId())
                .userType(OrderOperateUserTypeEnum.USER.getValue())
                .operateType(dto.getOperateType()).createBy(dto.getUserName()).build();
    }

    //保存订单日志
    void saveOrderLog(Long orderId, Long userId, String userName, OrderOperateTypeEnum operateTypeEnum) {
        AppOrderLogDTO orderLog = AppOrderLogDTO.builder()
                .orderId(orderId)
                .userId(userId)
                .userName(userName)
                .operateType(operateTypeEnum.getValue())
                .build();
        // 订单日志保存
        appOrderLogService.insertOrderLog(buildOrderLog(orderLog));
    }


    @Override
    public TableDataInfo list(AppOrderPageReqVO vo) {
        List<Order> orders = orderMapper.selectList01(vo);
        List<AppOrderPageRespVO> result = OrderConvert.INSTANCE.convertList(orders);
        if (CollectionUtils.isNotEmpty(result)) {
            // 查询订单明细
            List<Long> orderIds = result
                    .stream().map(AppOrderPageRespVO::getId).collect(Collectors.toList());
            List<AppOrderDetailPageRespVO> orderDetails = this.getOrderDetails(orderIds);
            orderDetails.forEach(res -> {
                String image = res.getImage();
                if (image == null || image.isEmpty()) {
                    res.setImage(res.getProductImage().split(",")[0]);
                }
            });
            Map<Long, List<AppOrderDetailPageRespVO>> orderDetailMap = orderDetails
                    .stream().collect(Collectors.groupingBy(AppOrderDetailPageRespVO::getOrderId));

            WebsiteSetting settingCache = this.getSettingCache();
            // 获取订单自动取消时间
            Integer orderCancelHour = settingCache.getOrderCancelHour();
            for (AppOrderPageRespVO res : result) {
                Long id = res.getId();
                Integer status = res.getStatus();
                res.setDetails(orderDetailMap.get(id));
                if (OrderStatusEnum.AWAITING_PAYMENT.getValue().equals(status)) {
                    res.setTime(getResiduePayTime(res.getCreateTime(), orderCancelHour));
                }
                Date completeTime = res.getCompleteTime();
                res.setCanComment(
                        this.judgmentOrderCommentStatus(
                                id, status, completeTime, settingCache.getPassDayRejectComment()
                        )
                );
                res.setCanAfterSale(this.checkAfterState(status, completeTime));
            }
        }
        return new TableDataInfo(result, PageUtils.getTotal(orders));
    }

    //判断订单是否可以评价
    private boolean checkAfterState(Integer status, Date completeTime) {
        return appAfterSalesService.checkAfterState(status, completeTime);
    }

    /**
     * 获取订单详情列表
     */
    private List<AppOrderDetailPageRespVO> getOrderDetails(List<Long> orderIds) {
        return appOrderDetailService.getOrderDetail(orderIds);
    }

    // 查询订单明细
    private List<OrderDetail> getOrderDetailById(Long orderId) {
        return appOrderDetailService.getOrderDetailByOrderId(orderId);
    }

    /**
     * 获取剩余支付时间
     *
     * @return
     */
    private long getResiduePayTime(Timestamp createTime, Integer orderCancelHour) {
        return createTime.getTime() / 1000 + orderCancelHour * 60 * 60 - System.currentTimeMillis() / 1000;
    }

    @Override
    public AppOrderInfoRespVO get(Long orderId, Long userId) {
        Order order = selectOrderById(orderId);
        // 校验是否为本人订单
        verifyMyselfOrder(userId, order.getUserId());
        BigDecimal taxation = order.getTaxation() == null ? BigDecimal.ZERO : order.getTaxation();
        BigDecimal postageTaxation = order.getPostageTaxation() == null ? BigDecimal.ZERO : order.getPostageTaxation();
        order.setTaxation(taxation.add(postageTaxation)); // 计算总税费
        AppOrderInfoRespVO vo = OrderConvert.INSTANCE.convert(order);

        WebsiteSetting settingCache = this.getSettingCache();
        //剩余支付时间
        vo.setTime(getResiduePayTime(vo.getCreateTime(), settingCache.getOrderCancelHour()));
        // 获取订单明细
        List<AppOrderDetailPageRespVO> orderDetails = this.getOrderDetails(Arrays.asList(orderId));
        orderDetails.forEach(res -> {
            String image = res.getImage();
            if (image == null || image.isEmpty()) {
                res.setImage(res.getProductImage().split(",")[0]);
            }
        });
        vo.setDetails(orderDetails);
        // 获取操作记录
        vo.setLogs(appOrderLogService.getOrderLogList(orderId));
        Integer status = order.getStatus();
        Date completeTime = order.getCompleteTime();
        //是否可评论订单
        vo.setCanComment(
                this.judgmentOrderCommentStatus(
                        orderId, status, completeTime, settingCache.getPassDayRejectComment()
                )
        );
        vo.setCanAfterSale(this.checkAfterState(status, completeTime));
        return vo;
    }

    @Override
    public Boolean getPaymentStatus(Long orderId) {
        return CommonWhetherEnum.YES.getValue().equals(selectOrderById(orderId).getPaid());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Order cancel(Long orderId, LoginUser loginUser) {
        // 查询订单
        Order order = selectOrderById(orderId);
        // 校验是否为本人订单
        Long userId = order.getUserId();
        verifyMyselfOrder(loginUser.getUserid(), userId);
        // 只有待付款状态可以取消订单
        if (!OrderStatusEnum.AWAITING_PAYMENT.getValue().equals(order.getStatus()))
            throw ServiceExceptionUtil.exception(ORDER_CANNOT_CANCEL_ERROR);
        // 修改订单状态
        this.updateOrderById(Order.builder()
                .id(orderId)
                .status(OrderStatusEnum.CANCELED.getValue())
                .paid(PaymentStatusEnum.PAYMENT_STATUS_UNPAID.getCode())
                .build()
        );
        // 查询订单明细
        List<OrderDetail> orderDetails = this.getOrderDetailById(orderId);
        List<Long> cartIds = orderDetails.stream().map(OrderDetail::getCartId).distinct().collect(Collectors.toList());
        // 购物车修改状态
        this.cartPayUpdate(cartIds);
        // 处理库存
        this.handleStock(getOrderCartInfo(cartIds), CommonWhetherEnum.NO);
        // 回退优惠券
        if (ObjectUtil.isNotNull(order.getCouponId()))
            appProductCouponRelationService.returnCoupon(order.getCouponId());
        // 退回积分
        Integer useIntegral = order.getUseIntegral();
        if (useIntegral != null && useIntegral > 0) {
            this.saveIntegralDetail(userId, useIntegral, IntegralBillTypeEnum.CANCEL);
        }
        // 订单日志保存
        this.saveOrderLog(orderId, userId, loginUser.getUsername(), OrderOperateTypeEnum.CANCEL_ORDER);
        return order;
    }

    //更新订单
    private void updateOrderById(Order order) {
        orderMapper.updateById(order);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(Long orderId) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserid(0L);
        loginUser.setUsername("admin");
        try {
            Order order = this.cancel(orderId, loginUser);
            MessageTemplateReplaceDTO param = MessageTemplateReplaceDTO
                    .builder()
                    .orderCode(order.getOrderCode())
                    .orderId(orderId)
                    .build();
            messageTemplateService.sendMessageText(MessageTemplateEnum.TEMPLATE_6, param, order.getUserId());
        } catch (Exception e) {
            log.error("rabbitmq 订单取消失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receipt(Long orderId, LoginUser loginUser) {
        // 查询订单
        Order order = selectOrderById(orderId);
        // 校验是否为本人订单
        verifyMyselfOrder(loginUser.getUserid(), order.getUserId());
        // 只有待收货状态可以取消订单
        if (!OrderStatusEnum.AWAITING_RECEIPT.getValue().equals(order.getStatus())) {
            throw ServiceExceptionUtil.exception(ORDER_CANNOT_RECEIPT_ERROR);
        }
        // 修改为已收货状态
        this.updateOrderById(Order.builder()
                .id(orderId)
                .completeTime(websiteSettingService.getDataBaseNow())
                .status(OrderStatusEnum.COMPLETED.getValue())
                .build()
        );
        // 订单日志保存
        this.saveOrderLog(orderId, loginUser.getUserid(), loginUser.getUsername(), OrderOperateTypeEnum.CONFIRM_RECEIPT);
        // 订单完成之后，超过售后之后赠送积分
        messageProducer.sendDelayedMessage(
                String.valueOf(orderId),
                RabbitMessageTypeEnum.DELAY_ORDER_AFTER_SALE_CLOSE,
                this.getSettingCache().getCloseAfterSale(),
                TimeUnit.DAYS
        );
    }

    // 获取积分规则
    private IntegralRule getRuleCache() {
        return integralService.getRuleCache();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receipt(Long orderId) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserid(0L);
        loginUser.setUsername("admin");
        try {
            this.receipt(orderId, loginUser);
        } catch (Exception e) {
            log.error("rabbitmq 订单确认收货失败", e);
        }
    }

    @Override
    public void paySuccess(String orderCode, String latestCharge, String paymentIntentId, String payType) {
        // 查询订单
        Order order = orderMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderCode, orderCode));
        if (ObjectUtil.isNull(order)) {
            throw ServiceExceptionUtil.exception(ORDER_NOT_EXIST_ERROR);
        }
        Long orderId = order.getId();
        // 修改为已收货状态
        this.updateOrderById(Order.builder()
                .id(orderId)
                .status(OrderStatusEnum.AWAITING_SHIPMENT.getValue())
                .payTime(Timestamp.valueOf(LocalDateTime.now()))
                .payType(payType)
                .paid(PaymentStatusEnum.PAYMENT_STATUS_PAID.getCode())
                .latestCharge(latestCharge)
                .paymentIntentId(paymentIntentId)
                .build()
        );
        Long userId = order.getUserId();
        // 查询用户信息
        MemberUser user = memberUserService.selectMemberUserById(userId);
        //支付成功更新用户消费信息
        memberUserService.updateConsume(user.getId(), order.getPayPrice());
        // 订单日志保存
        this.saveOrderLog(orderId, userId, user.getUsername(), OrderOperateTypeEnum.COMPLETE_PAYMENT);
        //触发自动客户分群
        messageProducer.sendMessage(String.valueOf(userId), RabbitMessageTypeEnum.MEMBER_GROUP_UPDATE_BY_USER_ACTION);
    }

    @Override
    public AppOrderPurchaseAgainRespVO purchaseAgain(Long orderId) {
        // 查询订单
        Order order = selectOrderById(orderId);
        // 查询订单明细
        List<OrderDetail> orderDetails = this.getOrderDetailById(orderId);
        // 获取购物车id
        List<Long> cartIds = orderDetails.stream().map(OrderDetail::getCartId).distinct().collect(Collectors.toList());
        // 查询购物车信息
        List<ShopCart> shopCarts = appShopCartService.selectShopCartByCartIds(cartIds);
        shopCarts.forEach(res -> res.setIsNew(CommonWhetherEnum.YES.getValue()));
        // 批量保存
        appShopCartService.batchInsert(shopCarts);
        // 获取保存的购物车id
        List<Long> result = shopCarts.stream().map(ShopCart::getCartId).collect(Collectors.toList());
        return new AppOrderPurchaseAgainRespVO(result);
    }

    @Override
    public void delete(Long orderId) {
        // 查询订单
        Order order = selectOrderById(orderId);
        // 判断状态
        if (!(OrderStatusEnum.CANCELED.getValue().equals(order.getStatus()) ||
                OrderStatusEnum.COMPLETED.getValue().equals(order.getStatus()))) {
            throw ServiceExceptionUtil.exception(ORDER_NOT_DELETE_ERROR);
        }
//        orderMapper.deleteById(orderId);
        Order del = new Order();
        del.setId(orderId);
        del.setUserDel(SwitchEnum.DISABLED.getCode());
        this.updateOrderById(del);
    }

    @Override
    public JSONArray getLogistics(String deliveryId) {
        LogisticsLog logisticsLog = logisticsLogService.selectByDeliveryId(deliveryId);
        if (ObjectUtil.isNotNull(logisticsLog)) {
            return JSONUtil.parseArray(logisticsLog.getLogisticsInfo());
        }
        return new JSONArray();
    }

    @Override
    public void updatePayStatus(Long orderId, Integer paymentStatus) {
        orderMapper.update(null, Wrappers.<Order>lambdaUpdate().eq(Order::getId, orderId).set(Order::getPaid, paymentStatus));
    }

    @Override
    public void payFailed(String orderCode) {
        // 查询订单
        Order order = this.selectOrderByOrderCode(orderCode);
        if (ObjectUtil.isNull(order)) {
            throw ServiceExceptionUtil.exception(ORDER_NOT_EXIST_ERROR);
        }
        Long orderId = order.getId();
        // 修改为未支付
        this.updateOrderById(Order.builder()
                .id(orderId)
                .paid(PaymentStatusEnum.PAYMENT_STATUS_UNPAID.getCode())
                .build()
        );
    }

    @Override
    public Order selectOrderById(Long orderId) {
        Order order = orderMapper.selectById(orderId);
        if (ObjectUtil.isNull(order)) throw ServiceExceptionUtil.exception(ORDER_NOT_EXIST_ERROR);
        return order;
    }

    @Override
    public Order selectOrderByOrderCode(String orderCode) {
        return orderMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderCode, orderCode));
    }

    /**
     * 查询订单购物车信息
     *
     * @param cartIds
     * @return
     */
    private List<AppOrderCartRespVO> getOrderCartInfo(List<Long> cartIds) {
        List<AppOrderCartRespVO> carts = appShopCartService.getOrderCartInfo(cartIds);
        if (CollectionUtils.isEmpty(carts)) throw ServiceExceptionUtil.exception(SHOP_CART_NOT_EXIST_ERROR);
        carts.forEach(res -> {
            String image = res.getImage();
            if (image == null || image.isEmpty()) {
                res.setImage(res.getProductImage().split(",")[0]);
            }
        });
        return carts;
    }

    /**
     * 计算订单数据
     *
     * @param carts
     * @return
     */
    private AppOrderInfoDTO computeOrder(List<AppOrderCartRespVO> carts, AppOrderCommonReqVO vo, WebsiteSetting setting) {
        AppOrderInfoDTO dto = new AppOrderInfoDTO();
        // 计算商品总数
        dto.setTotalNum(carts.stream().mapToInt(AppOrderCartRespVO::getNum).sum());
        // 计算商品总价
        dto.setTotalPrice(computeTotalPrice(carts));
        // 计算优惠金额
        dto.setDiscountAmount(computeCoupon(carts, vo));
        // 计算运费
        dto.setTotalPostage(computePostage(carts, vo, dto.getTotalPrice().subtract(dto.getDiscountAmount())));
        // 计算税费
        dto.setTaxation(computeSalesTaxation(carts, setting.getSalesTaxRate()));
        // 计算运费税费
        dto.setPostageTaxation(computeFreightRate(dto.getTotalPostage(), setting.getFreightRate()));
        // 订单的金额，还没减去积分抵扣，下面再计算积分减免金额
        BigDecimal total = dto.getTotalPrice()
                .subtract(dto.getDiscountAmount())
                .add(dto.getTotalPostage())
                .add(dto.getTaxation())
                .add(dto.getPostageTaxation());
        //积分规则
        IntegralRuleVO rule = IntegralConvert.INSTANCE.convert(this.getRuleCache());
        dto.setIntegralRule(rule);
        //用户积分余额
        Integer integralBalance = integralService.getUserIntegral(vo.getUserId());
        dto.setIntegralBalance(integralBalance);
        //使用积分的可选项
        List<BigDecimal> integralOptions = buildUseIntegralOptions(
                rule, new BigDecimal(integralBalance), total
        );
        dto.setUseIntegralOptions(integralOptions);

        BigDecimal payIntegral = vo.getPayIntegral();
        //如果使用了积分并且是正确的值
        if (payIntegral != null
                && rule.getIsUse().equals(OrderUseIntegralEnum.USE.getCode())
                && !integralOptions.isEmpty()
        ) {
            if (payIntegral.compareTo(integralOptions.get(0)) == 0) {
                // 支付积分值
                dto.setPayIntegral(payIntegral);
                //积分抵扣金额
                dto.setIntegralDeduct(integralOptions.get(1));
            } else {
                throw ServiceExceptionUtil.exception(ORDER_INTEGRAL_USE_ERROR);
            }
        }
        // 计算应付金额=总价-优惠金额+运费+基本税费+运费税费  -积分减免金额
        dto.setPayPrice(total.subtract(dto.getIntegralDeduct()));
        return dto;
    }

    /**
     * 计算商品总价
     *
     * @param carts
     * @return
     */
    private BigDecimal computeTotalPrice(List<AppOrderCartRespVO> carts) {
        return carts.stream().map(res -> res.getPrice().multiply(new BigDecimal(res.getNum())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 计算优惠券金额
     *
     * @param carts
     * @param vo
     */
    private BigDecimal computeCoupon(List<AppOrderCartRespVO> carts, AppOrderCommonReqVO vo) {
        BigDecimal couponAmount = BigDecimal.ZERO;
        // 计算优惠券
        if (ObjectUtil.isNotNull(vo.getCouponId()) || CommonWhetherEnum.YES.getValue().equals(vo.getIsDefaultCoupon())) {
            List<CartCouponDTO> cartCouponDtoList = new ArrayList<>();
            for (AppOrderCartRespVO cart : carts) {
                cartCouponDtoList.add(new CartCouponDTO(cart.getProductId(), cart.getSkuId(),
                        NumberUtil.mul(cart.getPrice(), cart.getNum())));
            }
            List<CartCouponRespVO> cartCouponVOList =
                    appProductCouponRelationService.searchCartCoupon(cartCouponDtoList, vo.getCouponId(), vo.getUserId());
            if (CollectionUtils.isNotEmpty(cartCouponVOList)) {
                CartCouponRespVO cartCouponVO = cartCouponVOList.get(0);
                couponAmount = cartCouponVO.getDiscountAmount();
                vo.setCouponId(cartCouponVO.getDetailId()); //返回优惠券明细id
                List<CartCouponDetailRespVO> cartCouponDetailVOList = cartCouponVO.getDetailVOList();
                if (CollectionUtils.isNotEmpty(cartCouponDetailVOList)) {
                    Map<Long, CartCouponDetailRespVO> cartCouponDetailVOMap =
                            cartCouponDetailVOList.stream().collect(Collectors
                                    .toMap(CartCouponDetailRespVO::getSkuId, Function.identity()));
                    carts.forEach(res -> {
                        if (ObjectUtil.isNotNull(cartCouponDetailVOMap.get(res.getSkuId()))) {
                            res.setRealPrice(res.getPrice().subtract(cartCouponDetailVOMap.get(res.getSkuId()).getProductCoupon()));
                        } else {
                            res.setRealPrice(res.getPrice());
                        }
                    });
                }
            }
        }
        return couponAmount;
    }

    /**
     * 计算运费
     *
     * @param carts
     * @param vo
     * @param orderAmount
     * @return
     */
    private BigDecimal computePostage(List<AppOrderCartRespVO> carts, AppOrderCommonReqVO vo, BigDecimal orderAmount) {
        // 查询地址
        UserAddress address = this.findUserAddress(vo.getAddressId(), vo.getUserId());
        if (ObjectUtil.isNull(address)) return BigDecimal.ZERO;
        vo.setAddressId(address.getAddressId());
        // 计算重量
        BigDecimal weight = carts.stream().map(res -> res.getWeight()
                .multiply(new BigDecimal(res.getNum()))).reduce(BigDecimal::add).get();
        List<Long> producerIds = carts.stream().map(AppOrderCartRespVO::getProductId).collect(Collectors.toList());
        return iShippingTemplatesService.calculatePrice(FreightComputeDTO.builder()
                .productIds(producerIds).wight(weight).orderAmount(orderAmount).regionId(address.getProvinceId()).build(), vo);
    }


    /**
     * 计算消费税费
     *
     * @param carts
     * @param salesTaxRate
     * @return
     */
    private BigDecimal computeSalesTaxation(List<AppOrderCartRespVO> carts, BigDecimal salesTaxRate) {
        BigDecimal totalSalesTaxation = BigDecimal.ZERO;    //总税费
        for (AppOrderCartRespVO cart : carts) {
            if (CommonWhetherEnum.YES.getValue().equals(cart.getTax())) {
                // 计算单件商品税费
                cart.setTaxation(cart.getRealPrice().multiply(salesTaxRate).divide(new BigDecimal(100)));
                // 计算总税费
                totalSalesTaxation = totalSalesTaxation.add(cart.getTaxation().multiply(new BigDecimal(cart.getNum())));
            }
        }
        return totalSalesTaxation;
    }

    /**
     * 计算运费税费
     *
     * @param totalPostage
     * @param freightRate
     * @return
     */
    private BigDecimal computeFreightRate(BigDecimal totalPostage, BigDecimal freightRate) {
        return totalPostage.multiply(freightRate).divide(new BigDecimal(100));
    }

    /**
     * 生成订单号
     *
     * @return
     */
    private String getOrderCode() {
        return RedisUtils.getSerialNumber(OrderConstants.ORDER_CODE_PREFIX,
                OrderConstants.ORDER_CODE_REDIS_KEY, OrderConstants.DATE_FORMAT, OrderConstants.NUMBER_FORMAT);
    }

    /**
     * 校验是否为自己的订单
     *
     * @param loginUserId
     * @param orderUserId
     */
    @Override
    public void verifyMyselfOrder(Long loginUserId, Long orderUserId) {
        if (!loginUserId.equals(orderUserId) && !loginUserId.equals(0L))
            throw ServiceExceptionUtil.exception(NOT_MYSELF_ORDER_ERROR);
    }

    @Override
    public Long getOrderUserCount(Long planId, DateTime start, DateTime end, Integer paid) {
        QueryWrapper<Order> queryWrapper = buildQueryWrapper(planId, start, end, paid);
        queryWrapper.select("count(distinct user_id) as user_id");
        return orderMapper.selectOne(queryWrapper).getUserId();
    }


    @Override
    public Long getOrderCount(Long planId, DateTime start, DateTime end, Integer paid) {
        QueryWrapper<Order> queryWrapper = buildQueryWrapper(planId, start, end, paid);
        return orderMapper.selectCount(queryWrapper);
    }

    @Override
    public BigDecimal getOrderAmount(Long planId, DateTime start, DateTime end, Integer paid) {
        QueryWrapper<Order> queryWrapper = buildQueryWrapper(planId, start, end, paid);
        queryWrapper.select("ifnull(sum(total_price),0) as total_price");
        return orderMapper.selectOne(queryWrapper).getTotalPrice();
    }

    private QueryWrapper<Order> buildQueryWrapper(Long planId, DateTime start, DateTime end, Integer paid) {
        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(planId)) {
            queryWrapper.eq("plan_id", planId);
        }
        if (ObjectUtil.isNotNull(start) && ObjectUtil.isNotNull(end)) {
            queryWrapper.between("create_time", start, end);
        }
        if (ObjectUtil.isNotNull(paid)) {
            queryWrapper.eq("paid", paid);
        }
        return queryWrapper;
    }

    @Override
    @Transactional
    public int submitOrderComment(AppOrderCommentReqVO param, LoginUser loginUser) {
        Long orderId = param.getOrderId();
        Order order = this.selectOrderById(orderId);
        // 校验是否为本人订单
        verifyMyselfOrder(loginUser.getUserid(), order.getUserId());
        Integer status = order.getStatus();
        if (!this.judgmentOrderCommentStatus(
                orderId, status, order.getCompleteTime(), this.getSettingCache().getPassDayRejectComment()
        )
        ) {
            throw ServiceExceptionUtil.exception(ORDER_COMMENT_NOT_ALLOW_ERROR);
        }
        List<AppOrderCommentReqVO.Comment> commentSet = param.getCommentSet();
        List<Long> commentProductIds = commentSet.stream()
                .map(AppOrderCommentReqVO.Comment::getProductId)
                .collect(Collectors.toList());
        List<Long> commentSkuIds = commentSet.stream()
                .map(AppOrderCommentReqVO.Comment::getSkuId)
                .collect(Collectors.toList());

        List<OrderDetail> orderDetails = this.getOrderDetailById(orderId);
        for (OrderDetail orderDetail : orderDetails) {
            Long detailProductId = orderDetail.getProductId();
            Long skuId = orderDetail.getSkuId();
            if (!commentProductIds.contains(detailProductId) || !commentSkuIds.contains(skuId)) {
                throw ServiceExceptionUtil.exception(ORDER_DETAIL_NOT_EXIST_ERROR);
            }
        }

        int ok = 0;
        Date commentTime = websiteSettingService.getDataBaseNow();
        String username = loginUser.getUsername();
        for (AppOrderCommentReqVO.Comment item : commentSet) {
            ProductCommentAddParam insertObj = ProductCommentAddParam.builder()
                    .productId(item.getProductId())
                    .orderId(orderId)
                    .commentUser(username)
                    .commentTime(commentTime)
                    .skuId(item.getSkuId())
                    .comment(item.getComment())
                    .score(item.getScore())
                    .imageUrls(item.getImageUrls())
                    .build();
            int i = productCommentService.submitOrderComment(insertObj);
            ok += i;
        }
        return ok;
    }

    /**
     * 判断订单是否可以评价
     */
    private boolean judgmentOrderCommentStatus(Long orderId, Integer status, Date completeTime, Integer passDay) {
        if (!OrderStatusEnum.COMPLETED.getValue().equals(status) || completeTime == null) {
            return false;
//            throw ServiceExceptionUtil.exception(ORDER_NOT_COMMENT_ERROR);
        }
        ProductComment orderComment = findCommentByOrderId(orderId);
        if (orderComment != null) {
            return false;
//            throw ServiceExceptionUtil.exception(ORDER_HAS_BEEN_COMMENT_ERROR);
        }
        //超过xx时间不能评论
        Date date = DateUtils.addDays(completeTime, passDay);
        //            throw ServiceExceptionUtil.exception(ORDER_COMMENT_OUT_TIME_ERROR);
        return !date.before(DateUtils.getNowDate());
    }

    /**
     * 通过订单号查询评论记录
     */
    public ProductComment findCommentByOrderId(Long orderId) {
        return productCommentService.findCommentByOrderId(orderId);
    }

    @Override
    public void waitPayNotice(Long orderId) {
        Order order = this.selectOrderById(orderId);
        //未支付则发出通知
        if (!order.getPaid().equals(PaymentStatusEnum.PAYMENT_STATUS_PAID.getCode())
                && order.getStatus().equals(OrderStatusEnum.AWAITING_PAYMENT.getValue())
        ) {
            int zoneGap = websiteSettingService.zoneTimeGap();
            WebsiteSetting websiteSetting = this.getSettingCache();
            Integer orderCancelHour = websiteSetting.getOrderCancelHour();
            Date date = DateUtils.addHours(order.getCreateTime(), -zoneGap + orderCancelHour);
            String limit = DateUtil.format(date, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            MessageTemplateReplaceDTO build = MessageTemplateReplaceDTO.builder()
                    .orderCode(order.getOrderCode())
                    .currencyUnit(websiteSetting.getCurrencySymbol())
                    .payPrice(order.getPayPrice().toString())
                    .orderId(orderId)
                    .timeLimit(limit)
                    .build();
            messageTemplateService.sendMessageText(MessageTemplateEnum.TEMPLATE_7, build, order.getUserId());
        }
    }

    /**
     * 自动收货前的通知
     *
     * @param orderId
     */
    @Override
    public void autoNoticeBeforeReceipt(Long orderId) {
        Order order = this.selectOrderById(orderId);
        MessageTemplateReplaceDTO build = MessageTemplateReplaceDTO.builder()
                .orderCode(order.getOrderCode())
                .orderId(orderId)
                .build();
        messageTemplateService.sendMessageText(MessageTemplateEnum.TEMPLATE_5, build, order.getUserId());
    }

    @Override
    public void sendIntegral(Long id) {
        Order order = selectOrderById(id);
        IntegralRule ruleCache = this.getRuleCache();
        Integer orderAward = ruleCache.getOrderAward();
        int price = order.getTotalPrice().intValue();
        if (orderAward > 0 && price > 0){
            this.saveIntegralDetail(order.getUserId(), orderAward * price, IntegralBillTypeEnum.ADD);
        }
    }

    // 插入积分详情
    private void saveIntegralDetail(Long userId, Integer integral, IntegralBillTypeEnum billTypeEnum) {
        integralService.insertIntegralDetail(userId, integral, billTypeEnum);
    }
}
